/**
 * 前置知识：
 * 1. 插件
 * 2. 混入
 * 3. Vue.observable()
 * 4. 插槽
 * 5. render 函数
 * 6. 运行时和完整版的 Vue
 * 7. history.pushState 只会改变浏览器地址栏中的地址并记录到历史记录
 * 实现分析：
 * 1. 注册插件：Vue.use(VueRouter) 中如果传入一个函数，会自动调用该函数，如果传入一个对象，会调用该函数的 install 方法
 * 2. 创建路由对象：const router = new VueRouter({}),  VueRouter 应该是一个类，接收路由参数（一个对象）
 * 3. 创建 Vue 实例，注册 router 对象， new Vue({ router, ... })
 * 分析结果(类图)，VueRouter 的组成：
 * 1. 属性
 *      options：记录构造函数传入的对象
 *      data：响应式对象，current 属性记录当前路由地址，需要用到 Vue.observable()
 *      routeMap：记录路由地址和组件的对应关系
 * 2. 方法
 *      Constructors(options): VueRouter  初始化接收的属性
 *      _install(Vue): void  静态方法，实现 插件 机制
 *      init(): void  用于调用以下三个方法
 *      initEvent(): void  注册 popState 事件，监听浏览器历史变化
 *      createRouteMap(): void  初始化 routeMap 属性，将接收的路由规则转化为 键（路由地址）值（对应组件）对 的形式，存储到 routeMap
 *      initComponents(Vue): void  创建 routerLink  routerView 组件
 */

let _Vue = null;

export default class VueRouter {
  static install(Vue) {
    // 1. 判断当前插件是否已安装
    if (VueRouter.install.installed) {
      return;
    }
    VueRouter.install.installed = true;

    // 2. 将 Vue 构造函数暴露到全局
    _Vue = Vue;

    // 3. 把传入的的 router 对象注入到 Vue 实例上
    // 混入：给所有的 vue 实例（除了组件）中混入一个选项，在 beforeCreate 钩子中挂载上路由对象，
    _Vue.mixin({
      beforeCreate() {
        if (!this.$options.router) return;
        _Vue.prototype.$router = this.$options.router;
        this.$options.router.init();
      }
    });
  }

  constructor(options) {
    this.options = options;
    this.routeType = options.type || "history";
    this.routeMap = {};
    this.data = _Vue.observable({
      // 响应式对象
      current: "/" // 存储当前的路由地址
    });
  }

  init() {
    this.createRouteMap();
    this.initComponents(_Vue);
    this.initEvent();
  }

  initEvent() {
    this.routeType === "history" &&
      addEventListener("popstate", () => {
        this.data.current = location.pathname;
      });
    this.routeType === "hash" &&
      addEventListener("hashchange", () => {
        this.data.current = location.hash.replace(/^\#/, '');
    });
  }

  createRouteMap() {
    // console.log(this.options);
    // 遍历所有的路由规则，并解析成键值对 存储到 routeMap
    this.options.routes.forEach(route => {
      this.routeMap[route.path] = route.component;
    });
  }

  initComponents(Vue) {
    const self = this;
    Vue.component("router-link", {
      props: {
        to: String
      },
      // template: '<a :href="to"><slot></slot></a>'  // 运行时版本不支持，实现以下 render 函数
      render(h) {
        console.log("route render", this);
        return h(
          "a",
          {
            attrs: {
              href: this.to
            },
            on: {
              click: this.clickHandler
            }
          },
          [this.$slots.default]
        );
        // })
      },
      methods: {
        clickHandler(e) {
          this.$router.routeType === "history"
            ? this.historyHandler()
            : this.hashHandler();
          this.$router.data.current = this.to;
          e.preventDefault();
        },
        historyHandler() {
          history.pushState({}, "", this.to);
        },
        hashHandler() {
        //   console.log(location);
          location = location.origin + "#" + this.to;
        }
      }
    });
    Vue.component("router-view", {
      render(h) {
        const component = self.routeMap[self.data.current];
        return h(component);
      }
      // template: '<div><slot></slot></div>'
    });
  }
}
